// ECMAScript
// ECMAScript也是一门脚本语言，通常看作JavaScript的标准化规范，实际上JavaScript是ECMAScript的扩展语言，ECMAScript只提供了最基本的语法。JavaScript本身指的就是ECMAScript
// JavaScript @ web等同于ECMAScript + Web APIs（BOM, DOM）
// JavaScript @ Node.js等同于ECMAScript +Node APIs（fs, net, etc.）

// const { resolve } = require("path")
// const { rejects } = require("assert")
// const { read } = require("fs")
// const { forEach } = require("lodash")
const { log } = console

// 1、解决原有语法上的一些问题或者不足
// 2、对原有语法进行增强
// 3、全新的对象、全新的方法、全新的功能
// 4、全新的数据类型和数据结构

// Nodemon

// let与块级作用域：某个成员能够起作用的范围
// ES2015之前只有 全局作用域，函数作用域，之后新增块级作用域
// let 存在块级作用域，在块级作用域中声明，在外部无法访问
// if (true) {
//   let foo = 'zce'
//   console.log(foo) // zce
// }

// -------------------------------------

// for (let i = 0;i < 3; i++) {
//   for (let i = 0;i < 3; i++) {
//     console.log(i) // 0 1 2 0 1 2 0 1 2
//   }
//   console.log('内层结束 i = ' + i) // 内层结束 i = 0 内层结束 i = 1 内层结束 i = 2
// }

// -------------------------------------

// var elements = [{}, {}, {}]
// for (var i = 0; i <elements.length; i++) {
//   elements[i].onclick = (function (i) {
//     return function() {
//       console.log(i)
//     }
//   })(i)
// }
// elements[2].onclick()

// -------------------------------------

// var elements = [{}, {}, {}]
// for (let i = 0; i <elements.length; i++) {
//   elements[i].onclick = function () {
//     console.log(i)
//   }
// }
// elements[0].onclick()

// -------------------------------------

// for (let i = 0; i < 3; i++) {
//   let i = 'foo'
//   console.log(i)
// }

// let i = 0

// if (i < 3) {
//   let i = 'foo'
//   console.log(i)
// }

// i++

// if (i < 3) {
//   let i = 'foo'
//   console.log(i)
// }

// i++

// if (i < 3) {
//   let i = 'foo'
//   console.log(i)
// }

// i++

// -----------------------------------

// let声明不会提升 const常量  声明之后不可修改,声明是必须赋值；
// console.log(foo)
// let foo = 'zce'

// const name = '123'
// name = 'jack' // 错误

// const name
// name = 'jack' // 错误

// const obj = {}
// obj.name = 'zce' // 正确

// obj = {} //错误

// 数组的结构
// const arr = [100, 200, 300]

// const foo = arr[0]
// const bar = arr[1]
// const baz = arr[2]
// console.log(foo, bar, baz) // 100 200 300

// const [foo, bar, baz] = arr
// console.log(foo, bar, baz)  // 100 200 300

// const [, , baz] = arr
// console.log(baz) // 300

// const [foo, ...rest] = arr
// console.log(rest) // [200, 300]

// const [foo] = arr
// console.log(foo) // 100

// const [foo, bar, baz, more] = arr
// console.log(more) // undefined

// const [foo, bar, baz, more = 'default value'] = arr
// console.log(more) // default value

// const path = '/foo/bar/baz'
// const tmp = path.split('/')
// const rootdir = tem[1] // foo

// const [, rootdir] = path.split('/')
// console.log(rootdir) // foo

// ----------------------------------------------------

// 对象的结构
// const obj = { name: 'fhc', age: 23}
// const { name, age } = obj
// console.log(name, age) // fhc 23

// const name = 'tom'
// const { name: objName = 'jack' } = obj // 可从命名 可赋默认值
// console.log(objName) // fhc

// 模板字符串 ``
// const str = `heel es2015, 

// this is a string`
// log(str) // 可以换行

// const name = 'tom'
// const msg = `hey, ${name} --- ${1 + 2} --- ${Math.random()}`
// log(msg) // hey, tom --- 3 --- 随机数

// 带标签的模板字符串

// const str = console.log`hello world` // [ 'hello world' ]
// const name = 'top'
// const gender = false
// function myTagFunc (strings, name, gender) {
//   // console.log(strings, name, gender)
//   // return '123'
//   const sex = gender ? 'man' : 'woman'
//   return strings[0] + name + strings[1] + sex + strings[2]
// }

// const result = myTagFunc`hey, ${name} is a ${gender}.`

// log(result) // hey, top is a woman.

// 字符串的扩展方法 includes() startsWith()  endsWith()

// const massage = 'Error: foo is not defined.'
// log(massage.startsWith('Error')) // true  字符串是不是以 Error 开头
// log(massage.endsWith('.')) // true  字符串是不是以 . 结尾
// log(massage.includes('foo')) // true  字符串中是否包含 foo


// 参数默认值
// function foo (enable) {
//   enable = enable === undefined ? true : enable
//   log('foo invoked - enable: ')
//   log(enable)
// }

// function foo (enable = true) {
//   log('foo invoked - enable: ')
//   log(enable)// false  如果不传参数就是默认值true
// }

// foo(false)

// 剩余参数
// function foo () {
//   log(arguments) // arguments所有参数的集合，是个伪数组
// }

// function foo (first, ...args) {
//   log(args) // [2, 3, 4]
// }
// foo(1, 2, 3, 4)

// 展开数组
// const arr = ['foo', 'bar', 'bzd']

// log(arr[0], arr[1], arr[2]) // foo bar bzd
// log.apply(console, arr) // foo bar bzd
// log(...arr) // foo bar bzd

// 箭头函数
// value => value; // 一个参数可以省略() 箭头后面不加{}相当于直接return
// (value, index) => {
//   return value[index] 
// }

// 对象字面量的增强
// const name = 'fhc'
// let obj = {
//   name,
//   fn () {} // 等同于 fn：function () {}
// }
// console.log(obj.name) // fhc

// object.assign 方法

// const source1 = {
//   a: 123,
//   b: 123
// }

//  const target = {
//    a: 456,
//    c: 456
//  }

//  const result = Object.assign(target, source1)

//  log(target) // { a: 123, c: 456, b: 123 }
//  log(result === target) // true

// object.is 判断两个值是否想的
// console.log(
//   0 == false,  // true
//   0 === false,  // false
//   +0 === -0, // true
//   NaN === NaN, // false
//   Object.is(NaN, NaN), // true
//   Object.is(+0, -0) // false
// )

// Object.defineProperty
// proxy
// const person = {
//   name: 'fhc',
//   age: 23
// }

// const personProxy = new Proxy(person, {
//   get (target, property) {
//     return property in target ? target[property] : 'default'
//     // console.log(target, property)
//     // return 100
//   },
//   set (target, property, value) {
//     if (property === 'age') {
//       if(!Number.isInteger(value)) {
//         throw new TypeError(`${value}`)
//       }
//     }
//     console.log(target, property, value)
//   }
// })

// personProxy.gender = true // { name: 'fhc', age: 23 } gender true
// personProxy.age = 24 // { name: 'fhc', age: 23 } age 24

// log(personProxy.name) // fhc
// log(personProxy.xxx) // default

// class类
// function Person (name) {
//   this.name = name
// }

// Person.prototype.say = function () {
//   console.log(`hi, my name is ${this.name}`)
// }

// class Person {
//   constructor (name) {
//     this.name = name
//   }

//   say () {
//     console.log(`hi, my name is ${this.name}`)
//   }
// }

// const p = new Person('tom')
// p.say()

// 实力方法vs.静态方法
// ES2015中新增添加静态方法的关键词static

// Person.prototype.say = function () {
//   console.log(`hi, my name is ${this.name}`)
// }

// class Person {
//   constructor (name) {
//     this.name = name
//   }

//   say () {
//     console.log(`hi, my name is ${this.name}`)
//   }

//   static create (name) {
//     return new Person(name)
//   }
// }

// const tom = Person.create('tom')
// tom.say()

// class 类的继承 extends

// class Person {
//   constructor (name) {
//     this.name = name
//   }

//   say () {
//     console.log(`hi, my name is ${this.name}`)
//   }
// }

// class Student extends Person {
//   constructor (name, number) {
//     super(name)
//     this.number = number
//   }

//   hello () {
//     super.say()
//     console.log(`my school number is ${this.number}`)
//   }
// }

// const s = new Student('jack', '100')
// s.hello()

// Set 数据结构 Set内部的元素不可以重复
// const s = new Set()
// s.add(1).add(2).add(3).add(4).add(2)
// console.log(s)

// s.forEach(i => console.log(i)) // 1  2  3  4

// for (let i of s) {
//   console.log(i) // 1  2  3  4
// }

// console.log(s.size) // 4 长度

// console.log(s.has(100)) //  has()是否包含

// console.log(s.delete(3)) // delete()删除

// s.clear() // 清除
// console.log(s)

// const arr = [1,2,1,3,4,1]

// const result = Array.from(new Set(arr)) // 数组去重
// const result = [...new Set(arr)] // 数组去重
// console.log(result) // [ 1, 2, 3, 4 ]

// Map 数据结构 可以使用任意类型作为键 对象只能使用字符串作为键
// const obj = {}
// obj[true] = 'value'
// obj[123] = 'value'
// obj[{a: 1}] = 'value'

// console.log(Object.keys(obj)) // [ '123', 'true', '[object Object]' ]  会转换成字符串
// console.log(obj['[object Object]']) // value

// const m = new Map()
// const tom = {name: 'tom'}
// m.set(tom, 90)
// console.log(m) // Map { { name: 'tom' } => 90 }
// console.log(m.get(tom)) // 90  get 获取某个键的值

// // m.has()  判断某个键
// // m.delete() 删除某个键
// // m.clear() 清空

// // 遍历方法
// m.forEach((value, key) => {
//   console.log(value, key) // 90 { name: 'tom' }
// })

// Symbol 数据类型 一个全新的原始类型 表示一个独一无二的值
// shared.js ================================

// const cache = {}

// // a.js =====================================

// cache['a_foo'] = Math.random()

// // b.js =====================================

// cache['b_foo'] = '123'
// console.log(cache)

// const s = Symbol()
// console.log(s) // Symbol()
// console.log(typeof s) // symbol

// console.log(
//   Symbol() === Symbol() // false
// )

// console.log(Symbol('foo')) // Symbol(foo)
// console.log(Symbol('bar')) // Symbol(bar)
// console.log(Symbol('baz')) // Symbol(baz)

// const obj = {}
// obj[Symbol()] = '123'
// obj[Symbol()] = '456'
// console.log(obj)

// const obj = {
//   [Symbol()]: 123
// }

// console.log(obj)

// // a.js =====================================

// const name = Symbol()
// const person = {
//   [name]: 'zce',
//   say() {
//     console.log(this[name])
//   }
// }

// person.say() // zce

const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(s1 === s2) // true

console.log(
  Symbol.for(true) === Symbol.for('true') // true
)

// console.log(Symbol.iterator)
// console.log(Symbol.hasInstance)

// const obj = {
//   [Symbol.toStringTag]: 'XObject'
// }
// console.log(obj.toString()) // [object XObject]

// const obj = {
//   [Symbol()]: 'symbol value',
//   foo: 'normal value'
// }

// for (var key in obj) {
//   console.log(key) // foo
// }
// console.log(Object.keys(obj)) // [ 'foo' ]
// console.log(JSON.stringify(obj)) // {"foo":"normal value"}

// console.log(Object.getOwnPropertySymbols(obj)) // [ Symbol() ]

// for···of 循环 伪数组也可以遍历
// const arr = [100, 200, 300, 400]
// for (const item of arr) {
//   console.log(item) // 100 200 300 400
// }

// for (const item of arr) {
//   console.log(item) // 100 200
//   if (item > 100) {
//     break
//   }
// }

// const s = new Set(['foo', 'bar'])
// for (const item of s) {
//   console.log(item) // foo bar
// }

// const m = new Map()
// m.set('foo', '123')
// m.set('bar', '345')

// for (const [ key, value ] of m) {
//   console.log(key, value) // foo 123   bar 345
// }

// const obj = { foo: '123', bar: '456' }
// for (const item of obj) {
//   console.log(item)
// }

// 可迭代接口
// 迭代器（Iterator）
// const set = new Set(['foo', 'bar', 'baz'])
// const iterator = set[Symbol.iterator]()
// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next())


// 实现可迭代接口
// const obj = {
//   store: ['foo', 'bar', 'baz'],

//   [Symbol.iterator]: function () {
//     let index = 0
//     const self = this
//     return {
//       next: function () {
//         const result =  {
//           value: self.store[index],
//           done: index >= self.store.length
//         }
//         index++
//         return result
//       }
//     }
//   }
// }

// for (const item of obj) {
//   console.log(item)
// }

// // 迭代器模式

// // 场景：你我协同开发的一个任务清单应用

// // 我的代码 ===============================

// const todos = {
//   life: ['吃饭', '睡觉', '打豆豆'],
//   learn: ['语文', '数学', '外语'],
//   work: ['喝茶'],

//   each: function (callback) {
//     const all = [].concat(this.life, this.learn, this.work)
//     for (const item of all) {
//       callback(item)
//     }
//   },

//   [Symbol.iterator]: function () {
//     const all = [...this.life, ...this.learn, ...this.work]
//     let index = 0
//     return {
//       next: function () {
//         return {
//           value: all[index],
//           done: index++ >= all.length
//         }
//       }
//     }
//   }
// }

// // 你的代码 ===============================


// todos.each(function (item) {
//   console.log(item)
// })

// console.log('----------------------------------')

// for (const item of todos) {
//   console.log(item)
// }

// 生成器 Generator
// function * foo () {
//   console.log('zce')
//   return 100
// }

// const result = foo()
// console.log(result.next()) // { value: 100, done: true }

// function * foo () {
//   console.log('1111')
//   yield 100
//   console.log('2222')
//   yield 200
//   console.log('3333')
//   yield 300
// }

// const generator = foo()
// console.log(generator.next()) // { value: 100, done: false }
// console.log(generator.next()) // { value: 200, done: false }
// console.log(generator.next()) // { value: 300, done: false }
// console.log(generator.next()) // { value: undefined, done: true }
// forEach // 不能跳出循环

// 生成器应用
// 案例1： 发号器
// function * createIdMaker() {
//   let id = 1
//   while (true) {
//     yield id++
//   }
// }
// const idMaker = createIdMaker()
// console.log(idMaker.next().value) // 1
// console.log(idMaker.next().value) // 2
// console.log(idMaker.next().value) // 3
// console.log(idMaker.next().value) // 4

// // 案例2：使用 Generator 函数实现 iterator 方法
// const todos = {
//   life: ['吃饭', '睡觉', '打豆豆'],
//   learn: ['语文', '数学', '外语'],
//   work: ['喝茶'],
//   [Symbol.iterator]: function * () {
//     const all = [...this.life, ...this.learn, ...this.work]
//     for (const item of all) {
//       yield item
//     }
//   }
// }

// for (const item of todos) {
//   console.log(item)
// }

// Array.prototype.includes
// const arr = ['foo', 1, NaN, false]
// console.log(arr.includes('foo')) // 检查数组中是否包含某元素

// // 指数运算符
// console.log(Math.pow(2, 10))  // 2的10次方
// console.log(2 ** 10) // 2的10次方


// ECMAScript 2017
const obj = {
  foo: 'value1',
  bar: 'value2'
}
// Object.values -----------------------

console.log(Object.values(obj)) // [ 'value1', 'value2' ]

// Object.entries -----------------------

console.log(Object.entries(obj)) // [ [ 'foo', 'value1' ], [ 'bar', 'value2' ] ]

for (const [key, value] of Object.entries(obj)) {
  console.log(key, value)
}

console.log(new Map(Object.entries(obj))) // Map { 'foo' => 'value1', 'bar' => 'value2' }

// Object.getOwnPropertyDescriptors -----------------------

const p1 = {
  firstName: 'Lei',
  lastName: 'Wang',
  get fullName () {
    return this.firstName + ' ' + this.lastName
  }
}

console.log(p1.fullName) // Lei Wang

const p2 = Object.assign({}, p1)
p2.fullName = 'zce'
console.log(p2)


// acdmqefgikjblnpho

// new Promise((resolve, reject) => {
//   console.log('A')
//   setTimeout(() => {
//     console.log('B')
//   }, 0)
//   console.log('C')
//   resolve()
//   console.log('D')
// }).then(() => {
//   console.log('E')
//   new Promise((resolve, reject) => {
//     console.log('F')
//     resolve()
//     console.log('G')
//   }).then(() => {
//     setTimeout(() => {
//       console.log('H')
//     }, 0)
//     console.log('I')
//   }).then(() => {
//     console.log('J')
//   })
// }).then(() => {
//   console.log('K')
// })

// setTimeout(() => {
//   console.log('L')
// }, 0)

// new Promise((resolve, reject) => {
//   console.log('M')
//   resolve()
// }).then(() => {
//   setTimeout(() => {
//     new Promise((resolve, reject) => {
//       console.log('N')
//       resolve()
//     }).then(() => {
//       setTimeout(() => {
//         console.log('O')
//       }, 0)
//     }).then(() => {
//       console.log('P')
//     })
//   }, 0)
// })

// console.log('Q')

// 【ID1004686】【优化】抖帮帮官网页脚文案 优化（直接上正式）
/*
b d
b c
a b d
b
a c d
a b d
a b
a b c
b c d
*/
